//! @author Adam Emil Skoog
//! @date   2012-01-05

#include "Body.h"
#include "Game.h"
#include "Level.h"

namespace mehdi
 {
	Body::Body()
	:
	horizontalSpeed(.0f),
	verticalSpeed  (.0f),
	fallTime       (.0),
	inAir          (true),
	gravitation    (true),
	collision      (true)
	 {
		Game::getLevel()->bodies.push_back(this);
	 }

	Body &Body::setHorizontalSpeed(const float &speed)
	 {
		horizontalSpeed = speed;
		return *this;
	 }

	const float &Body::getHorizontalSpeed() const
	 {
		return horizontalSpeed;
	 }

	Body &Body::setVerticalSpeed(const float &speed)
	 {
		verticalSpeed = speed;
		return *this;
	 }

	const float &Body::getVerticalSpeed() const
	 {
		return verticalSpeed;
	 }

	Body &Body::setReceptiveToGravity(const bool &set)
	 {
		gravitation = set;
		return *this;
	 }

	const bool &Body::isReceptiveToGravity() const
	 {
		return gravitation;
	 }

	Body &Body::setReceptiveToCollision(const bool &set)
	 {
		collision = set;
		return *this;
	 }

	const bool &Body::isReceptiveToCollision() const
	 {
		return collision;
	 }

	CollisionMask *const Body::createCollisionMask()
	 {
		CollisionMask *const result(new CollisionMask);
		masks.push_back(result);
		return result;
	 }

	bool Body::destroyCollisionMask(CollisionMask *const mask)
	 {
		for (unsigned i(0); i < masks.size(); ++ i)
		 {
			if (masks[i] == mask)
			 {
				delete mask;
				masks.erase(masks.begin() + i);
				return true;
			 }
		 }

		return false;
	 }

	bool Body::destroyCollisionMaskAt(const unsigned &index)
	 {
		if (static_cast<int>(index) < static_cast<int>(masks.size()))
		 {
			delete masks[index];
			masks.erase(masks.begin() + index);
			return true;
		 }

		return false;
	 }

	const std::vector<CollisionMask *> &Body::getCollisionMasks() const
	 {
		return masks;
	 }

	void Body::updatePhysics()
	 {
		position().x += horizontalSpeed * Game::getPlayFrameTime();

		if (isReceptiveToGravity())
			if (inAir)
				position().y += (verticalSpeed + (fallTime += Game::getPlayFrameTime())) * 730.f * Game::getPlayFrameTime();
	 }

	void Body::land(Body *const other)
	 {
		inAir         = false;
		fallTime      = .0;
		verticalSpeed = .0f;

		if (getCollisionMasks().size() > 0 && other->getCollisionMasks().size() > 0)
		 {
			if (position().y < other->position().y)
			 {
				float yBottom(getCollisionMasks()[0]->getPosition().y + getCollisionMasks()[0]->getSize().y),
					  yTop   (other->getCollisionMasks()[0]->getPosition().y);

				for (unsigned i(0); i < masks.size(); ++ i)
					if (masks[i]->getPosition().y > yBottom - masks[i]->getSize().y)
						yBottom = masks[i]->getPosition().y + masks[i]->getSize().y;

				for (unsigned i(0); i < other->masks.size(); ++ i)
					if (other->masks[i]->getPosition().y < yTop)
						yTop = other->masks[i]->getPosition().y;

				position().y = other->position().y + yTop - yBottom;
			 }
		 }

		onLand();
	 }

	Body::~Body()
	 {
		for (unsigned i(0); i < masks.size(); ++ i)
			delete masks[i];

		masks.clear();
	 }
 }
